import axios from 'axios'
import type { AxiosInstance, InternalAxiosRequestConfig, AxiosError, AxiosResponse } from 'axios'
import type { CostomRequestConfig, CostomInternalRequestConfig, resType, cancelToken, requestOption } from './type'
import { showResCode, showStatus, showRetCode } from './status'
import { router } from '@/router/index'
import { reverse } from 'lodash-es'
import storageUtil from '@/utils/storage_util'

class Request {
  // axios 实例
  instance: AxiosInstance
  cancelRequestList?: cancelToken[]
  type?: string
  constructor(config: CostomRequestConfig, type?: string) {
    this.instance = axios.create(config)
    this.cancelRequestList = []
    this.type = type
    /** 
     * 类拦截器
    */
    // 请求拦截器
    this.instance.interceptors.request.use(
      (config: InternalAxiosRequestConfig) => {
        config.headers['token'] = storageUtil.get('token') || '';
        return config
      },
      (error: AxiosError) => Promise.reject(error),
    )
    // 响应拦截器
    this.instance.interceptors.response.use(
      // 因为接口的数据都在res.data下，所以直接返回res.data
      (res: AxiosResponse) => {
        if (res.request.responseType === 'blob') {
          return res
        }
        return res.data
      },
      (error: AxiosError) => Promise.reject(error),
    )

    /**
     * 实例拦截器
     */
    this.instance.interceptors.request.use(
      config.interceptors?.requestSuccessFn,
      config.interceptors?.requestFailureFn
    )
    this.instance.interceptors.response.use(
      config.interceptors?.responseSuccessFn,
      config.interceptors?.responseFailureFn
    )
  }

  catchError(info: any, url: string | undefined, reject: any, type: 'res' | 'error') {
    if (type === 'res') {
      if (info.changePath) {
        router.push(info.changePath)
      }
      // Vue.prototype.$msgTips.warning(url+ msg);
      reject({ code: info.code, msg: info.msg })
    } else {
      if (axios.isCancel(info)) {
        console.error('[ 取消请求 ]: ' + url)
      } else {
        const msg: string = showStatus(info.code)
        // Vue.prototype.$msgTips.warning(url+ msg);
        reject({ code: info.code, msg })
      }
    }
  }
  // 自定义拦截
  customInterceptors<T = any>(config: CostomRequestConfig<T>, options?: requestOption) {
    if (config.interceptors?.requestSuccessFn) {
      // 类型“CostomRequestConfig<T>”的参数不能赋给类型“InternalAxiosRequestConfig<any>”的参数。
      config = config.interceptors.requestSuccessFn(config as CostomInternalRequestConfig)
    }
    // ignoreCancelToken: true 此请求不能被取消
    if (config.url && !options?.ignoreCancelToken) {
      config.cancelToken = new axios.CancelToken(c => {
        this.cancelRequestList?.push({
          cancel: c,
          url: config.url as string
        })
      })
    }
    return config
  }
  // 请求方法
  request<T = any>(config: CostomRequestConfig<T>, options?: requestOption): Promise<T> {
    config = this.customInterceptors(config, options)
    return new Promise((resolve, reject) => {
      this.instance.request<any, T>(config).then(res => {
        if (config.interceptors?.responseSuccessFn) {
          res = config.interceptors.responseSuccessFn(res)
        }
        let statusData
        if (this.type && this.type === 'room') { // 视频sdk封装
          statusData = showRetCode(res as resType);
        } else {
          statusData = showResCode(res as resType, options?.isResolve || false);
        }
        if (statusData.isResolve) {
          resolve(res);
        } else {
          this.catchError(statusData, config.url, reject, 'res')
        }
      }).catch((error) => {
        if (config.interceptors?.responseFailureFn) {
          error = config.interceptors.responseFailureFn(error);
        }
        this.catchError(error, config.url, reject, 'error')
      }).finally(() => {
        !options?.ignoreCancelToken && this.deleteRequest(config.url as string)
      })
    })
  }
  // 下载文件方法
  download<T = any>(config: CostomRequestConfig<T>, options?: requestOption): Promise<T> {
    config = this.customInterceptors(config, options)
    return new Promise((resolve, reject) => {
      this.instance.request<any, T>({ ...config, responseType: 'blob' }).then((res) => {
        if (config.interceptors?.responseSuccessFn) {
          res = config.interceptors.responseSuccessFn(res)
        }
        const data = (res as AxiosResponse).data
        const headers = (res as AxiosResponse).headers
        if (data.type === 'application/json') {
          const reader = new FileReader(); //创建一个FileReader实例
          reader.readAsText(data, 'utf-8'); //读取文件,结果用字符串形式表示
          reader.onload = () => {
            const { msg, code } = JSON.parse((reader.result) as string);
            const resData: resType = {
              msg,
              code: code || 400
            }
            const statusData = showResCode(resData, false);
            this.catchError(statusData, config.url, reject, 'res')
          };
        } else {
          const link = document.createElement('a');
          const blob = new Blob([data], { type: data.type || options?.fileType }); //文件流处理
          link.style.display = 'none'; //去除a标签的样式
          link.href = URL.createObjectURL(blob);
          link.download = (headers['content-disposition'] ? decodeURIComponent(headers['content-disposition']).split('filename=')[1] : options?.fileName) || '';
          document.body.appendChild(link);
          link.click();
          URL.revokeObjectURL(link.href);
          const resData = {
            msg: '文件下载成功',
            code: 200
          }
          resolve(resData as any);
        }
      }).catch(error => {
        if (config.interceptors?.responseFailureFn) {
          error = config.interceptors.responseFailureFn(error);
        }
        this.catchError(error, config.url, reject, 'error')
      }).finally(() => {
        !options?.ignoreCancelToken && this.deleteRequest(config.url as string)
      })
    })
  }
  // 取消全部请求 
  cancelAllRequest() {
    this.cancelRequestList?.forEach(item => {
      item.cancel()
    })
    this.cancelRequestList = []
  }
  deleteRequest(url: string) {
    const index = this.cancelRequestList ? this.cancelRequestList.findIndex(item => item.url === url) : -1
    if (index > -1) {
      this.cancelRequestList?.splice(index, 1)
    }
  }
  cancelRequest(url: string | string[]) {
    const indexList: number[] = []
    if (typeof url === 'string') {
      // 取消单个请求  
      this.cancelRequestList?.forEach((item, index) => {
        if (item.url === url) {
          item.cancel()
          indexList.push(index)
        }
      })
    } else {
      // 存在多个需要取消请求的地址  
      this.cancelRequestList?.forEach((item, index) => {
        if (url.findIndex(u => u === item.url) > -1) {
          item.cancel()
          indexList.push(index)
        }
      })
    }
    reverse(indexList).forEach((item: number) => {
      this.cancelRequestList?.splice(item, 1)
    })
  }
}

export default Request